// Copyright (c) 2007 Robin Boerdijk. All rights reserved.
//
// This software is licensed under the terms of the Microsoft Reciprocal License as described 
// in the file LICENSE.txt, which you should have received as part of this distribution. The 
// license terms are also available at http://www.opensource.org/licenses/ms-rl.html.

#include "wtk/DialogWindow.h"
#include "wtk/Control.h"

#include <cassert>

namespace wtk
{
	DialogWindow::DialogWindow(Window *parentWindow, const std::wstring &title)
		: Window(L"#32770", parentWindow)
		, title(title)
	{
		this->style |= WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE;
		this->styleEx |= WS_EX_COMPOSITED;
	}

	void DialogWindow::Create()
	{
		if (m_clientBox.get() == NULL) {
			m_clientBox.reset(CreateClientBox());
		}

		if ((m_clientBox->GetHorizontalElasticity() != 0) || (m_clientBox->GetVerticalElasticity() != 0)) {
			this->style |= WS_SIZEBOX;
		}

		SIZE initialClientSize(m_clientBox->GetMinimumSize());

		this->size = ClientSizeToWindowSize(initialClientSize);

		HWND parentWindowHandle = (this->parentWindow == NULL) ? win32::GetDesktopWindow() : this->parentWindow->handle;

		RECT parentWindowRect;
		win32::GetWindowRect(parentWindowHandle, &parentWindowRect);

		this->position = GetDefaultWindowPosition(this->size, parentWindowRect);

		Window::Create(this->title.c_str(), NULL);

		m_clientBox->Layout(initialClientSize);

		for (size_t i = 0, n = this->controls.size(); i < n; i++) {
			this->controls[i]->Create();
		}
	}

	void DialogWindow::SetDefaultFrameStyle(DWORD defaultFrameStyle)
	{
		const DWORD dialogFrameStyleEx = WS_EX_DLGMODALFRAME | WS_EX_TOOLWINDOW;

		if ((this->styleEx & dialogFrameStyleEx) == 0) {
			this->styleEx |= defaultFrameStyle;
		}
	}

	int DialogWindow::RunModal()
	{
		SetDefaultFrameStyle(WS_EX_DLGMODALFRAME);

		Create();
		
		bool disableEnableParentWindow = (this->parentWindow != NULL) && this->parentWindow->IsEnabled();

		if (disableEnableParentWindow) {
			this->parentWindow->Disable();
		}

		m_runningModeless = false;

		MSG msg;
		while (win32::GetMessageW(&msg, NULL, 0, 0)) {
			win32::TranslateMessage(&msg); 
			win32::DispatchMessageW(&msg); 
		}

		if (disableEnableParentWindow) {
			this->parentWindow->Enable();
		}

		Destroy();

		return msg.wParam;
	}

	// static
	void DialogWindow::RunModeless(DialogWindow *dialogWindow)
	{
		dialogWindow->SetDefaultFrameStyle(WS_EX_TOOLWINDOW);
		dialogWindow->Create();
		dialogWindow->m_runningModeless = true;
	}

	void DialogWindow::Exit(int exitCode)
	{
		if (m_runningModeless) {
			Destroy();
			delete this;
		} else {
			win32::PostQuitMessage(exitCode);
		}
	}

	SIZE DialogWindow::ClientSizeToWindowSize(const SIZE &clientSize)
	{
		RECT rect(Construct<RECT>(clientSize));
		win32::AdjustWindowRectEx(&rect, this->style, FALSE, this->styleEx);
		return Construct<SIZE>(rect);
	}

	void DialogWindow::CommitControlPositions()
	{
		UINT flags = SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOOWNERZORDER | SWP_NOZORDER;

		HDWP dwp = win32::BeginDeferWindowPos(this->controls.size());
		for (size_t i = 0, n = this->controls.size(); i < n; i++) {
			dwp = this->controls[i]->DeferWindowPos(dwp, NULL, flags);
		}
		win32::EndDeferWindowPos(dwp);
	}

	LRESULT DialogWindow::ProcessMessage(HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam)
	{
		switch (message)
		{
		case WM_CLOSE:
			return ProcessCloseMessage();
		case WM_SIZING:
			return ProcessSizingMessage(*reinterpret_cast<RECT *>(lParam));
		case WM_SIZE:
			return ProcessSizeMessage(Construct<SIZE>(LOWORD(lParam), HIWORD(lParam)));
		default:
			return Window::ProcessMessage(windowHandle, message, wParam, lParam);
		}
	}

	LRESULT DialogWindow::ProcessCloseMessage()
	{
		Exit(IDCANCEL);
		return 0;
	}

	LRESULT DialogWindow::ProcessSizingMessage(RECT &windowRect)
	{
		SIZE windowSize = Construct<SIZE>(windowRect);

		SIZE minimumClientSize = m_clientBox->GetMinimumSize();
		SIZE minimumWindowSize = ClientSizeToWindowSize(minimumClientSize);

		if ((windowSize.cx < minimumWindowSize.cx ) || (m_clientBox->GetHorizontalElasticity() == 0)) {
			windowRect.right = windowRect.left + minimumWindowSize.cx;
		}

		if ((windowSize.cy < minimumWindowSize.cy ) || (m_clientBox->GetVerticalElasticity() == 0)) {
			windowRect.bottom = windowRect.top + minimumWindowSize.cy;
		}

		return TRUE;
	}

	LRESULT DialogWindow::ProcessSizeMessage(const SIZE &clientSize)
	{
		m_clientBox->Layout(clientSize);
		CommitControlPositions();
		return 0;
	}
}
